package it.polimi.traveldream.ejb.eaos;

import java.sql.Timestamp;
import java.util.Date;
import java.util.List;

import it.polimi.traveldream.ejb.dtocreator.ParticipationDTOCreator;
import it.polimi.traveldream.ejb.entities.Excursion;
import it.polimi.traveldream.ejb.entities.Hotel;
import it.polimi.traveldream.ejb.entities.Participation;
import it.polimi.traveldream.ejb.entities.ParticipationPK;
import it.polimi.traveldream.ejb.entities.Transport;
import it.polimi.traveldream.ejb.entities.Travelpackage;
import it.polimi.traveldream.ejb.entities.User;
import it.polimi.traveldream.ejbclient.dtos.ParticipationDTO;
import it.polimi.traveldream.ejbclient.eaos.ParticipationServiceEAO;
import it.polimi.traveldream.utils.enums.UserRole;
import it.polimi.traveldream.utils.exceptions.NotAvailableException;
import it.polimi.traveldream.utils.exceptions.NotSuchProductException;
import it.polimi.traveldream.utils.exceptions.NotSuchUserException;

import javax.annotation.security.RolesAllowed;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

/**
 * Session Bean implementation class ParticipationService
 */
@Stateless
public class ParticipationService implements ParticipationServiceEAO {
	@PersistenceContext
	private EntityManager entityManager;

	@RolesAllowed({UserRole._CUSTOMER,UserRole._EMPLOYEE})
	@SuppressWarnings("unchecked")
	@Override
	public List<ParticipationDTO> getUserParticipations(int customerId, boolean overpast)
			throws NotSuchUserException {
		Query q = entityManager.createNamedQuery(Participation.FIND_USER_PARTICIPATIONS);
		q.setParameter("customerId", customerId);
		q.setParameter("overpast", overpast);
		List<Participation> participations = q.getResultList();
		if(!participations.isEmpty()) {
			return ParticipationDTOCreator.createParticipationList(participations);
		} else throw new NotSuchUserException();
	}

	@RolesAllowed({UserRole._EMPLOYEE})
	@SuppressWarnings("unchecked")
	@Override
	public List<ParticipationDTO> getParticipations(boolean overpast) {
		Query q = entityManager.createNamedQuery(Participation.FIND_PARTICIPATIONS);
		q.setParameter("overpast", overpast);
		return ParticipationDTOCreator.createParticipationList(q.getResultList());
	}

	@RolesAllowed({UserRole._CUSTOMER})
	@Override
	public void confirmParticipation(int customerId, int packageId, int reservations)
			throws NotSuchUserException, NotSuchProductException, NotAvailableException {
		Travelpackage travelPackage = entityManager.find(Travelpackage.class, packageId);
		if(travelPackage != null) {
			User user = entityManager.find(User.class, customerId);
			if(user != null) {
				entityManager.refresh(travelPackage);
				int feasibleReservations = travelPackage.getFeasibleReservations();
				Date currentDate = new Date();
				if(feasibleReservations - reservations >= 0 && !travelPackage.getOverpast() && currentDate.before(travelPackage.getExpiry())) {
					updateReservations(travelPackage, reservations);
					Participation participationAssociated = existJustParticipation(customerId, travelPackage);
					if(participationAssociated == null) {
						participationAssociated = new Participation();
						ParticipationPK pk = new ParticipationPK();
						pk.setCustomer(user.getId());
						pk.setTravelPackage(travelPackage.getId());
						participationAssociated.setId(pk);
						participationAssociated.setUser(user);
						participationAssociated.setTravelpackage(travelPackage);
						participationAssociated.setReservations(reservations);
						participationAssociated.setTimestamp(new Timestamp(new Date().getTime()));
						travelPackage.addParticipation(participationAssociated);
						entityManager.persist(participationAssociated);
					} else {
						int actualReservations = participationAssociated.getReservations();
						participationAssociated.setReservations(reservations + actualReservations);
						entityManager.merge(participationAssociated);
					}
					entityManager.flush();
				} else throw new NotAvailableException(feasibleReservations);
			} else throw new NotSuchUserException();
		} else throw new NotSuchProductException();
	}

	@RolesAllowed({UserRole._EMPLOYEE})
	@SuppressWarnings("unchecked")
	@Override
	public List<ParticipationDTO> searchParticipations(int packageId, boolean overpast)
			throws NotSuchProductException {
		Query q = entityManager.createNamedQuery(Participation.FIND_PARTICIPATIONS_BY_PACKAGE);
		q.setParameter("packageId", packageId);
		q.setParameter("overpast", overpast);
		List<Participation> participations = q.getResultList();
		if(!participations.isEmpty()) {
			return ParticipationDTOCreator.createParticipationList(participations);
		} else throw new NotSuchProductException();
	}

	private Participation existJustParticipation(int customerId, Travelpackage travelPackage)
			throws NotSuchUserException {
		for(Participation p : travelPackage.getParticipations()) {
			if(p.getUser().getId() == customerId) {
				return p;
			}
		}
		return null;
	}
	
	private void updateReservations(Travelpackage travelPackage, int reservations) {
		updateReservationsHotels(travelPackage, reservations);
		updateReservationsTransports(travelPackage, reservations);
		updateReservationsExcursions(travelPackage, reservations);
		entityManager.merge(travelPackage);
	}

	private void updateReservationsExcursions(Travelpackage travelPackage, int reservations) {
		int feasibleReservationsPackage;
		int feasibleReservationsBaseProduct;
		for(Excursion e : travelPackage.getExcursions()) {
			e.setReservations(e.getReservations() + reservations);
			for(Travelpackage tp : e.getTravelpackages()) {
				feasibleReservationsPackage = tp.getFeasibleReservations();
				feasibleReservationsBaseProduct = e.getMaxAvailability() - e.getReservations();
				if(feasibleReservationsPackage > feasibleReservationsBaseProduct) {
					tp.setFeasibleReservations(feasibleReservationsBaseProduct);
					entityManager.merge(tp);
				}
			}
		}
	}

	private void updateReservationsTransports(Travelpackage travelPackage, int reservations) {
		int feasibleReservationsPackage;
		int feasibleReservationsBaseProduct;
		for(Transport t : travelPackage.getTransports()) {
			t.setReservations(t.getReservations() + reservations);
			for(Travelpackage tp : t.getTravelpackages()) {
				feasibleReservationsPackage = tp.getFeasibleReservations();
				feasibleReservationsBaseProduct = t.getMaxAvailability() - t.getReservations();
				if(feasibleReservationsPackage > feasibleReservationsBaseProduct) {
					tp.setFeasibleReservations(feasibleReservationsBaseProduct);
					entityManager.merge(tp);
				}
			}
		}
	}

	private void updateReservationsHotels(Travelpackage travelPackage, int reservations) {
		int feasibleReservationsPackage;
		int feasibleReservationsBaseProduct;
		for(Hotel h : travelPackage.getHotels()) {
			h.setReservations(h.getReservations() + reservations);
			for(Travelpackage tp : h.getTravelpackages()) {
				feasibleReservationsPackage = tp.getFeasibleReservations();
				feasibleReservationsBaseProduct = h.getMaxAvailability() - h.getReservations();
				if(feasibleReservationsPackage > feasibleReservationsBaseProduct) {
					tp.setFeasibleReservations(feasibleReservationsBaseProduct);
					entityManager.merge(tp);
				}
			}
		}
	}
	
}
